#include "../../includes/network/cserialportmanager.h"
#include "../../includes/QsLog/QsLog.h"

#include <QtSerialPort/QSerialPortInfo>
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>

initialiseSingleton(CSerialPortManager);

CSerialPort::CSerialPort(NetworkFrameManager *pNetworkFrameManager,QObject *parent) :
    QSerialPort(parent),
    m_NetworkFrameManager(pNetworkFrameManager)
{
    connect(this, SIGNAL(error(QSerialPort::SerialPortError)), this,
            SLOT(handleError(QSerialPort::SerialPortError)));

    connect(this, SIGNAL(readyRead()), this, SLOT(readData()));
}


CSerialPort::~CSerialPort()
{

}

/**
 * @brief CSerialPort::openSerialPort 打开串口
 * @param PortName 串口名
 * @param BaudRate 波特率
 * @param DataBits 数据位数
 * @param Parity 奇偶校验
 * @param StopBits 停止位
 * @param FlowControl 流控制
 *
 * @return 如果串口打开成功返回真，否则返回假
 */
bool CSerialPort::openSerialPort(QString PortName,qint32 BaudRate,QSerialPort::DataBits DataBits,QSerialPort::Parity Parity,QSerialPort::StopBits StopBits,QSerialPort::FlowControl FlowControl)
{
    //设置串口名
    this->setPortName(PortName);
    //设置波特率
    this->setBaudRate(BaudRate);
    //设置数据位数
    this->setDataBits(DataBits);
    //设置奇偶校验
    this->setParity(Parity);
    //设置停止位
    this->setStopBits(StopBits);
    //设置流控制
    this->setFlowControl(FlowControl);

    //打开串口
    if(!this->open(QIODevice::ReadWrite))
    {
        QLOG_ERROR()<<"SerialPort:"<<PortName<<" open fail.";
        return false;
    }

    qDebug()<<"Open SerialPort success:"<<PortName
           <<" "<<BaudRate
           <<" "<<DataBits
           <<" "<<Parity
           <<" "<<StopBits
           <<" "<<FlowControl;

    QLOG_INFO()<<"Open SerialPort success:"<<PortName
              <<" "<<BaudRate
              <<" "<<DataBits
              <<" "<<Parity
              <<" "<<StopBits
              <<" "<<FlowControl;

    return true;
}

/**
 * @brief CSerialPort::openArduinoSerialPort 查找系统中Arduino专用串口，并打开
 */
void CSerialPort::openArduinoSerialPort()
{
    QSerialPortInfo portToUse;
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QString s = QObject::tr("Port:") + info.portName() + "\n"
                    + QObject::tr("Location:") + info.systemLocation() + "\n"
                    + QObject::tr("Description:") + info.description() + "\n"
                    + QObject::tr("Manufacturer:") + info.manufacturer() + "\n"
                    + QObject::tr("Serial number:") + info.serialNumber() + "\n"
                    + QObject::tr("Vendor Identifier:") + (info.hasVendorIdentifier() ? QString::number(info.vendorIdentifier(), 16) : QString()) + "\n"
                    + QObject::tr("Product Identifier:") + (info.hasProductIdentifier() ? QString::number(info.productIdentifier(), 16) : QString()) + "\n"
                    + QObject::tr("Busy:") + (info.isBusy() ? QObject::tr("Yes") : QObject::tr("No")) + "\n";

        if(!info.isBusy() && (info.description().contains("Arduino") || info.manufacturer().contains("Arduino")))
            portToUse = info;
        qDebug() << s;
        QLOG_INFO() << "CSerialPortManager::openSerialPort:" << s;
    }

    if(portToUse.isNull() || !portToUse.isValid())
    {
        qDebug() << "port is not valid:" << portToUse.portName();
        QLOG_ERROR() << "port is not valid:" << portToUse.portName();
        return;
    }

    this->setPortName(portToUse.portName());
    this->setBaudRate(QSerialPort::Baud115200);
    this->setDataBits(QSerialPort::Data8);
    this->setParity(QSerialPort::NoParity);
    this->setStopBits(QSerialPort::OneStop);
    this->setFlowControl(QSerialPort::NoFlowControl);

    if (this->open(QIODevice::ReadWrite)) {
        qDebug() << "Connected to" << portToUse.description() << "on" << portToUse.portName();
        QLOG_INFO() << "Connected to" << portToUse.description() << "on" << portToUse.portName();
    } else {
        qCritical() << "Serial Port error:" << this->errorString();
        QLOG_ERROR() << "Serial Port error:" << this->errorString();
    }
}

/**
 * @brief CSerialPort::closeSerialPort 关闭串口
 */
void CSerialPort::closeSerialPort()
{
    this->close();
}

qint64 CSerialPort::writeData(const QByteArray &data)
{
    if(data.isEmpty())
        return -1;

    return this->write(data);
}

/**
 * @brief CSerialPort::sendJson 发送json数据
 * @param mes 要发送的数据
 */
void CSerialPort::sendJson(QJsonObject mes)
{
    if(mes.isEmpty())
        return;

    QJsonDocument document=QJsonDocument(mes);
    QByteArray array = document.toJson();// 转换成QByteArray

    this->SendData(array);
}

/**
 * @brief CSerialPort 发送二进制数据
 */
bool CSerialPort::SendData(QByteArray &data)
{
    if(data.isEmpty())
        return false;

    qint64 m_sendsize,m_totalsize;

    tagPacketHearder pTcpPacketHearder;
    pTcpPacketHearder.version = IDD_SERIALPORT_VERSION;
    pTcpPacketHearder.size = data.size();

    QByteArray sendData;

    //添加包头
    sendData.append((const char*)&pTcpPacketHearder,sizeof(tagPacketHearder));

    //添加包数据
    sendData.append(data);

    m_totalsize = sendData.size();
    m_sendsize=0;

    do
    {
        QByteArray psendbytearray;

        if(sendData.size()-m_sendsize < SERIALPORT_BUF_SIZE)
            psendbytearray = sendData.mid(m_sendsize);
        else
            psendbytearray = sendData.mid(m_sendsize,SERIALPORT_BUF_SIZE);

        m_sendsize += writeData(psendbytearray);

        QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
    }
    while(m_sendsize < m_totalsize);

    return true;
}

void CSerialPort::readData()
{
    m_dataPacket.append(this->readAll());

    // 循环解析包数据，数据中可能不只一包数据
    parsePacket();
}

void CSerialPort::handleError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError) {
        qCritical() << "Serial Port error:" << this->errorString();
        QLOG_ERROR() << "Serial Port error:" << this->errorString();

        closeSerialPort();
    }
}

/**
 * @brief CSerialPort::parsePacket 解包
 * @param packet 要解包的数据
 *
 * @return 如果数据解包成功返回真，否则返回假
 */
bool CSerialPort::parsePacket(void)
{
    while(!m_dataPacket.isEmpty())
    {
        if(!m_recvDataState && m_dataPacket.size() >= sizeof(tagPacketHearder))
        {
            // 先取包头
            memcpy(&m_PacketHearder,m_dataPacket.constData(),sizeof(tagPacketHearder));

            // 检查版本号是否正确
            if(m_PacketHearder.version != IDD_SERIALPORT_VERSION)
                break;

            m_dataPacket.remove(0,sizeof(tagPacketHearder));
            m_recvDataState = true;
        }

        if(!m_recvDataState || m_dataPacket.size() < m_PacketHearder.size)
            break;

        // 得到当前数据
        QByteArray precvData = m_dataPacket.mid(0,m_PacketHearder.size);
        m_dataPacket.remove(0,m_PacketHearder.size);

        if(m_NetworkFrameManager)
            m_NetworkFrameManager->OnProcessSerialPortBinary(this,precvData);

        m_recvDataState=false;
    }

    return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

CSerialPortManager::CSerialPortManager()
{

}

CSerialPortManager::~CSerialPortManager()
{
    clearAllSerialPorts();
}

/**
 * @brief CSerialPortManager::addSerialPort 添加一个串口
 * @param name 串口名称
 * @param pSerialPort 要添加的串口
 * @return 如果串口添加成功返回真，否则返回假
 */
bool CSerialPortManager::addSerialPort(QString name,CSerialPort* pSerialPort)
{
    if(name.isEmpty() || pSerialPort == NULL || m_SerialPortList.isEmpty())
        return false;

    QHash<QString,CSerialPort*>::iterator iter = m_SerialPortList.find(name);
    if(iter == m_SerialPortList.end())
        return false;

    m_SerialPortList[name] = pSerialPort;

    return true;
}

/**
 * @brief CSerialPortManager::getSerialPort 得到一个串口
 * @param name 要得到的串口的名称
 * @return 如果存在这个串口返回这个串口，否则返回NULL
 */
CSerialPort* CSerialPortManager::getSerialPort(QString name)
{
    if(name.isEmpty())
        return NULL;

    QHash<QString,CSerialPort*>::iterator iter = m_SerialPortList.find(name);
    if(iter == m_SerialPortList.end())
        return NULL;

    return iter.value();
}

/**
 * @brief CSerialPortManager::clearAllSerialPorts 清除所有的串口
 */
void CSerialPortManager::clearAllSerialPorts(void)
{
    if(m_SerialPortList.isEmpty()) return;

    QHash<QString,CSerialPort*>::iterator iter = m_SerialPortList.begin();
    for(;iter != m_SerialPortList.end();++iter)
    {
        delete iter.value();
    }

    m_SerialPortList.clear();
}

/**
 * @brief CSerialPortManager::deleteSerialPort 清除指定的串口
 * @param name 要清除的串口名称
 * @return 如果串口清除成功返回真，否则返回假
 */
bool CSerialPortManager::deleteSerialPort(QString name)
{
    if(name.isEmpty())
        return false;

    QHash<QString,CSerialPort*>::iterator iter = m_SerialPortList.find(name);
    if(iter != m_SerialPortList.end())
    {
        delete iter.value();
        m_SerialPortList.erase(iter);

        return true;
    }

    return false;
}
